﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using AOUtils.Libraries;
using System.Drawing;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Remoting.Messaging;
using System.IO;
using System.Net.Cache;
using System.Xml.Serialization;
using System.Xml;
using System.Threading;
using System.Windows.Threading;
using System.ComponentModel;
using System.Collections;

namespace AOUtils
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Runner : Window
    {
        private Dictionary<EquipmentPage, EquipmentGrid> grids = new Dictionary<EquipmentPage, EquipmentGrid>();
        private delegate void ProgressReported(int percent);
        private delegate void UpdateProgressBarHandler(long i);
        private delegate void UpdateTextBoxHandler(string message, bool append);
        private delegate void EnableControlsHandler(bool state);
        private delegate void ItemSourceHandler(ItemsControl control, IEnumerable itemsSource);
        private delegate void PlaceItemOnGridHandler(OptimizedItem item, Grid grid);
        private delegate void OptimizeSelectionHandler(Grid grid);
        private delegate void CreateStatsHandler();
        private ItemsHandler itemshandler = new ItemsHandler();
        private ItemSearcher searcher = new ItemSearcher();

        public Runner()
        {
            InitializeComponent();
            grids.Add(EquipmentPage.Armor, clothingGrid);
            grids.Add(EquipmentPage.Weapon, weaponGrid);
            grids.Add(EquipmentPage.Implant, implantGrid);
            grids.Add(EquipmentPage.Spirit, implantGrid);
            itemshandler.ProgressUpdate += new EventHandler<ProgressUpdateEventArgs>(ProgressUpdate);
            itemshandler.ResourcesLoaded += new EventHandler<EventArgs>(ResourcesLoaded);
            searcher.ProgressUpdate += new EventHandler<ProgressUpdateEventArgs>(ProgressUpdate);
            searcher.ItemFound += new EventHandler<ItemFoundEventArgs>(ItemFound);
            searcher.SearchFinished += new EventHandler<EventArgs>(SearchFinished);
        }

        private void SearchFinished(object sender, EventArgs e)
        {
            EnableControls(true);
            OptimizeSelection(grids[EquipmentPage.Armor]);
            OptimizeSelection(grids[EquipmentPage.Weapon]);
            OptimizeSelection(grids[EquipmentPage.Implant]);
            CreateStats();
        }
        
        private void CreateStats()
        {
            SortedDictionary<string, int> skills = new SortedDictionary<string, int>();
            List<string> skillindex = new List<string>();
            //KeyValuePair<string, int> kvp;

            if (Dispatcher.CheckAccess())
            {
                foreach (IconDockPanel panel in grids[EquipmentPage.Armor].Children)
                {
                    OptimizedItem item = panel.SelectedItem;
                    if (item == null)
                        continue;
                    foreach (KeyValuePair<string, int> kvp in item.GetSkillValues())
                    {
                        if (!skills.ContainsKey(kvp.Key))
                        {
                            skillindex.Add(kvp.Key);
                            skills.Add(kvp.Key, kvp.Value);
                        }
                        else
                            skills[kvp.Key] += kvp.Value;
                    }
                }
                foreach (IconDockPanel panel in grids[EquipmentPage.Implant].Children)
                {
                    OptimizedItem item = panel.SelectedItem;
                    if (item == null)
                        continue;
                    foreach (KeyValuePair<string, int> kvp in item.GetSkillValues())
                    {
                        if (!skills.ContainsKey(kvp.Key))
                        {
                            skillindex.Add(kvp.Key);
                            skills.Add(kvp.Key, kvp.Value);
                        }
                        else
                            skills[kvp.Key] += kvp.Value;
                    }
                }
                foreach (IconDockPanel panel in grids[EquipmentPage.Weapon].Children)
                {
                    OptimizedItem item = panel.SelectedItem;
                    if (item == null)
                        continue;
                    foreach (KeyValuePair<string, int> kvp in item.GetSkillValues())
                    {
                        if (!skills.ContainsKey(kvp.Key))
                        {
                            skillindex.Add(kvp.Key);
                            skills.Add(kvp.Key, kvp.Value);
                        }
                        else
                            skills[kvp.Key] += kvp.Value;
                    }
                }
                skillindex.Sort();
                StringBuilder tmp = new StringBuilder();
                for (int i = 0; i < skills.Count/3; i++)
                {
                    if (skills.Count % 3 == 0)
                        tmp.AppendFormat("{0,-25}:{1,7} | {2,-25}:{3,7} | {4,-25}:{5,7}" + Environment.NewLine, skillindex[i], skills[skillindex[i]], skillindex[i + (skills.Count / 3)], skills[skillindex[i + (skills.Count / 3)]], skillindex[i + skills.Count / 3 * 2], skills[skillindex[i + skills.Count / 3 * 2]]);
                    else if (skills.Count % 3 == 1)
                        tmp.AppendFormat("{0,-25}:{1,7} | {2,-25}:{3,7} | {4,-25}:{5,7}" + Environment.NewLine, skillindex[i], skills[skillindex[i]], skillindex[i + (skills.Count / 3) + 1], skills[skillindex[i + (skills.Count / 3) + 1]], skillindex[i + skills.Count / 3 * 2 + 1], skills[skillindex[i + skills.Count / 3 * 2 + 1]]);
                    else
                        tmp.AppendFormat("{0,-25}:{1,7} | {2,-25}:{3,7} | {4,-25}:{5,7}" + Environment.NewLine, skillindex[i], skills[skillindex[i]], skillindex[i + (skills.Count / 3) + 1], skills[skillindex[i + (skills.Count / 3) + 1]], skillindex[i + skills.Count / 3 * 2 + 2], skills[skillindex[i + skills.Count / 3 * 2 + 2]]);
                }
                if (skills.Count % 3 == 1)
                    tmp.AppendFormat("{0,-25}:{1,7}", skillindex[skills.Count / 3], skills[skillindex[skills.Count / 3]]);
                if (skills.Count % 3 == 2)
                    tmp.AppendFormat("{0,-25}:{1,7} | {2,-25}:{3,7}", skillindex[skills.Count / 3], skills[skillindex[skills.Count / 3]], skillindex[skills.Count / 3 * 2 + 1], skills[skillindex[skills.Count / 3 * 2 + 1]]);
                textBox1.Text = tmp.ToString();
            }
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new CreateStatsHandler(CreateStats));
        }

        private void ItemFound(object sender, ItemFoundEventArgs e)
        {
            PlaceItemOnGrid(e.Item, grids[e.Item.EquipmentPage]);
        }

        private void ResourcesLoaded(object sender, EventArgs e)
        {
            SetItemSource(skillsListBox, itemshandler.Skills);
            EnableControls(true);
        }

        private void SetItemSource(ItemsControl control, IEnumerable itemsSource)
        {
            if (Dispatcher.CheckAccess())
                control.ItemsSource = itemsSource;
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new ItemSourceHandler(SetItemSource), control, itemsSource);
        }

        private void ProgressUpdate(object sender, ProgressUpdateEventArgs e)
        {
            UpdateTextBox(e.Message, true);
            UpdateProgressBar(e.Number);
        }

        private void UpdateTextBox(string message, bool append)
        {
            if (Dispatcher.CheckAccess())
            {
                if (message != string.Empty)
                {
                    if (append)
                        textBox1.Text += message + Environment.NewLine;
                    else
                        textBox1.Text = message + Environment.NewLine;
                }
            }
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new UpdateTextBoxHandler(UpdateTextBox), message, append);
        }

        private void UpdateProgressBar(long i)
        {
            if (Dispatcher.CheckAccess())
                progressBar.Value = i;
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new UpdateProgressBarHandler(UpdateProgressBar), i);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            EnableControls(false);
            Thread t = new Thread(new ThreadStart(itemshandler.LoadResources));
            t.Start();
        }

        private void EnableControls(bool state)
        {
            if (Dispatcher.CheckAccess())
            {
                /*if (state)
                    searchButton.Content = "Search";
                else
                    searchButton.Content = "Cancel";*/
                searchButton.IsEnabled = state;
                clearButton.IsEnabled = state;
                professionComboBox.IsEnabled = state;
                breedComboBox.IsEnabled = state;
                skillsListBox.IsEnabled = state;
                factionComboBox.IsEnabled = state;
                useSocialArmorCheckBox.IsEnabled = state;
            }
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new EnableControlsHandler(EnableControls), state);
        }

        private void searchButton_Click(object sender, RoutedEventArgs e)
        {
            ClearForm();
            if (skillsListBox.SelectedValue == null)
            {
                MessageBox.Show("No skill selected", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            EnableControls(false);

            SearchCriteria criteria = new SearchCriteria();
            criteria.Breed = (Breed)breedComboBox.SelectedValue;
            criteria.Profession = (Professions)professionComboBox.SelectedValue;
            criteria.Faction = (Faction)factionComboBox.SelectedValue;
            criteria.Skill = skillsListBox.SelectedValue.ToString();
            criteria.UseSocial = (bool)useSocialArmorCheckBox.IsChecked;
            criteria.Xml = itemshandler.Xml;
            Thread t = new Thread(new ParameterizedThreadStart(searcher.Search));
            t.Start(criteria);
        }
        private void ReplaceItem(OptimizedItem currentItem,Grid grid,int gridIndex, int contextMenuIndex)
        {
            bool isPlaced = false;
            for (int i = 0; i < gridIndex; i++)
            {
                IconDockPanel panel = (IconDockPanel)grid.Children[i];
                if (panel.SelectedItem == null || !panel.SelectedItem.Name.Equals(currentItem.Name))
                    continue;
                MenuItem menuItem = (MenuItem)panel.ContextMenu.Items[panel.SelectedIndex+1];
                OptimizedItem item = (OptimizedItem)menuItem.Tag;
                menuItem = (MenuItem)((IconDockPanel)grid.Children[gridIndex]).ContextMenu.Items[contextMenuIndex+1];
                OptimizedItem nextCurrentItem = (OptimizedItem)menuItem.Tag;
                if (item == null && nextCurrentItem == null)
                {
                    isPlaced = true;
                    break;
                }
                if (item == null && nextCurrentItem != null)
                {
                    CheckPlacement(nextCurrentItem, (IconDockPanel)grid.Children[gridIndex], gridIndex, contextMenuIndex + 1);
                    isPlaced = true;
                    break;
                }
                if (item != null && nextCurrentItem == null)
                {
                    CheckPlacement(item, panel, i, panel.SelectedIndex + 1);
                    isPlaced = true;
                    break;
                }
                if (item != null && nextCurrentItem != null && item.GetSkillValues()[currentItem.SortSkill] > nextCurrentItem.GetSkillValues()[currentItem.SortSkill])
                {
                    ((IconDockPanel)grid.Children[gridIndex]).SelectedItem = currentItem;
                    CheckPlacement(item, panel, i, panel.SelectedIndex + 1);
                    isPlaced = true;
                    break;
                }
                if (item != null && nextCurrentItem != null && item.GetSkillValues()[currentItem.SortSkill] == nextCurrentItem.GetSkillValues()[currentItem.SortSkill])
                {
                    CheckPlacement(nextCurrentItem, (IconDockPanel)grid.Children[gridIndex], gridIndex, contextMenuIndex + 1);
                    isPlaced = true;
                    break;
                }
                if (item != null && nextCurrentItem != null && item.GetSkillValues()[currentItem.SortSkill] < nextCurrentItem.GetSkillValues()[currentItem.SortSkill])
                {
                    ((IconDockPanel)grid.Children[gridIndex]).SelectedItem = nextCurrentItem;
                    isPlaced = true;
                    break;
                }
            }
            if(!isPlaced)
                ((IconDockPanel)grid.Children[gridIndex]).SelectedItem = currentItem;
        }

        public void CheckPlacement(OptimizedItem item, IconDockPanel panel, int gridIndex, int contextMenuIndex)
        {
            if (contextMenuIndex == 0 && !item.Unique)
            {
                panel.SelectedItem = item;
            }
            else if ((item.Slot & ~panel.Slot) == Slot.None)
            {
                panel.SelectedItem = item;
            }
            else
            {
                ReplaceItem(item, (Grid)panel.Parent, gridIndex, contextMenuIndex);
            }
        }

        public void OptimizeSelection(Grid grid)
        {
            if (Dispatcher.CheckAccess())
            {
                for (int x = 0; x < grid.Children.Count; x++)
                {
                    IconDockPanel panel = (IconDockPanel)grid.Children[x];
                    if (panel.ContextMenu != null)
                    {
                        OptimizedItem item = (OptimizedItem)((MenuItem)panel.ContextMenu.Items[0]).Tag;
                        if (item != null)
                        {
                            CheckPlacement(item, panel, x, 0);
                        }
                        else
                        {
                            panel.SelectedItem = item;
                        }
                    }
                }
            }
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new OptimizeSelectionHandler(OptimizeSelection), grid);
        }

        private MenuItem CreateMenuItem(OptimizedItem item)
        {
            MenuItem mi = new MenuItem();
            mi.Tag = item;
            mi.Header = item.Name + " (" + item.GetSkillValues()[item.SortSkill] + ")";
            mi.ToolTip = item.Name + Environment.NewLine + "Ql " + item.Ql + Environment.NewLine;
            if (item.Unique)
                mi.ToolTip += "Unique" + Environment.NewLine;
            if (item.Social)
                mi.ToolTip += "Social" + Environment.NewLine;
            mi.ToolTip += Environment.NewLine;

            if (item.OnWear.Functions[FunctionType.Modify].Targets.Contains(TargetType.Wearer))
            {
                for (int i = 0; i < item.OnWear.Functions[FunctionType.Modify].Targets[TargetType.Wearer].Arguments.Count; i += 2)
                {
                    OptimizedArgument arg1 = item.OnWear.Functions[FunctionType.Modify].Targets[TargetType.Wearer].Arguments[i];
                    OptimizedArgument arg2 = item.OnWear.Functions[FunctionType.Modify].Targets[TargetType.Wearer].Arguments[i + 1];
                    if (arg1.Requirements.Count > 0)
                    {
                        foreach (OptimizedRequirement req in arg1.Requirements)
                        {
                            mi.ToolTip += req.Statistic + " " + req.Operator.ToString() + " " + req.Value + " " + req.ChildOp + Environment.NewLine;
                        }
                    }
                    mi.ToolTip += "\t" + arg1.Value + " " + arg2.Value + Environment.NewLine;
                }
            }
            if (item.OnWear.Functions[FunctionType.Modify].Targets.Contains(TargetType.Target))
            {
                for (int i = 0; i < item.OnWear.Functions[FunctionType.Modify].Targets[TargetType.Target].Arguments.Count; i += 2)
                {
                    OptimizedArgument arg1 = item.OnWear.Functions[FunctionType.Modify].Targets[TargetType.Target].Arguments[i];
                    OptimizedArgument arg2 = item.OnWear.Functions[FunctionType.Modify].Targets[TargetType.Target].Arguments[i + 1];
                    if (arg1.Requirements.Count > 0)
                    {
                        foreach (OptimizedRequirement req in arg1.Requirements)
                        {
                            mi.ToolTip += req.Statistic + " " + req.Operator.ToString() + " " + req.Value + " " + req.ChildOp + Environment.NewLine;
                        }
                    }
                    mi.ToolTip += "\t" + arg1.Value + " " + arg2.Value + Environment.NewLine;
                }
            }
            return mi;
        }

        private void PlaceItemOnGrid(OptimizedItem item, Grid grid)
        {

            if (Dispatcher.CheckAccess())
            {
                foreach (IconDockPanel panel in grid.Children)
                {
                    if ((panel.Slot & item.Slot) > 0)
                    {
                        panel.AddItem(CreateMenuItem(item));
                    }
                }
            }
            else
                Dispatcher.BeginInvoke(DispatcherPriority.Normal, new PlaceItemOnGridHandler(PlaceItemOnGrid), item, grid);   
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            ClearForm();
        }

        private void ClearForm()
        {
            textBox1.Text = string.Empty;
            foreach (IconDockPanel panel in weaponGrid.Children)
            {
                panel.ClearItems();
            }
            foreach (IconDockPanel panel in clothingGrid.Children)
            {
                panel.ClearItems();
            }
            foreach (IconDockPanel panel in implantGrid.Children)
            {
                panel.ClearItems();
            }
        }

        private void SelectedItemChangedHandler(object sender, ItemChangedEventArgs e)
        {
            CreateStats();
        }
    }
}
